#include "PageCache.h"
#include "CentralCache.h"

//从spanlist 或者Page Cache获取一个span
Span *CentralCache::GetOneSpan(size_t size)
{
    size_t index = SizeClass::ListIndex(size);
    SpanList &spanlist = spanlists[index];
    Span *it = spanlist.Begin();
    while (it != spanlist.End()) {
        if (!it->freelist.Empty()) {
            return it;
        } else {
            it = it->next;
        }
    }

    // page cache 获取一个span
    spanlist.Unlock();
    size_t numpage = SizeClass::NumMovePage(size);
    // 这一步很重要，申请span和修改span状态必须是原子操作，注意isUse只能在释放时修改
    PageCache::GetPageCacheInstance().Lock();
    Span *span = PageCache::GetPageCacheInstance().NewSpan(numpage);
    span->objsize = size;
    span->isUse = true;
    PageCache::GetPageCacheInstance().Unlock();

    char *start = (char *)(span->pageid << PAGE_SHIFT);
    char *end = start + (span->pagenums << PAGE_SHIFT);
    while (start < end) {
        char *obj = start;
        start += size;
        span->freelist.Push(obj);
    }
    spanlist.Lock();
    spanlist.PushFront(span);
    return span;
}

//从中心缓存获取一定数量的对象给ThreadCache
size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t num, size_t size)
{
    size_t index = SizeClass::ListIndex(size);
    SpanList &spanlist = spanlists[index];
    spanlist.Lock();

    Span *span = GetOneSpan(size);
    assert(span);
    FreeList &freelist = span->freelist;
    size_t actualNum = freelist.PopRange(start, end, num);
    span->usecount += actualNum;
    spanlist.Unlock();

    return actualNum;
}

//将一定数量的对象释放到span跨度
void CentralCache::ReleseListToSpans(void *start, size_t size)
{
    size_t index = SizeClass::ListIndex(size);
    SpanList &spanlist = spanlists[index];
    spanlist.Lock();

    // std::cout << "central cache release list to spans, size:" << size << " start:" << start << std::endl;
    while (start) {
        // std::cout << "in ReleseListToSpans while" << std::endl;
        void *next = NextObj(start);
        PAGE_ID id = (PAGE_ID)start >> PAGE_SHIFT;
        Span *span = PageCache::GetPageCacheInstance().GetIdToSpan(id);
        span->freelist.Push(start);
        span->usecount--;

        // 表示当前span切出去的对象全部返回，可以将span还给page cache
        if (span->usecount == 0) {
            spanlist.Erase(span);
            span->next = nullptr;
            span->prev = nullptr;
            span->freelist.Clear();

            spanlist.Unlock();
            PageCache::GetPageCacheInstance().ReleaseSpanToPageCache(span);
            spanlist.Lock();
        }
        start = next;
    }
    spanlist.Unlock();
}